package com.mathdroid.gui.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.util.LinkedList;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.JToolTip;
import javax.swing.SwingConstants;

import com.mathdroid.core.DroidStringWorker;
import com.mathdroid.gui.ImageLoader;
import com.mathdroid.model.enums.EModuleProficiency;
import com.mathdroid.model.items.Item;

/**
 * This tooltip is shown when hovering over item icons. By default it displays
 * the standard tipText. If an item is added to this ToolTip, it displays a
 * second row with the name of the item. If the item has modules, the modules
 * are listed below a JSeparator.
 */
public final class ItemToolTip extends JToolTip {

	/**
	 * Default UID.
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * The content panel of this tooltip.
	 */
	private final JPanel p = new JPanel(new GridLayout(0, 1));
	
	/**
	 * The constraints for the content panel.
	 */
	private final GridBagConstraints c = new GridBagConstraints();
	
	/**
	 * The baby blue background color.
	 */
	private static final Color BABY_BLUE = new Color(200, 221, 242);
	
	/**
	 * The item to show information about.
	 */
	private Item item = null;
	
	/**
	 * The label that displays the item type.
	 */
	private final JLabel lblItemType = new JLabel();
	
	/**
	 * The label that shows the item name.
	 */
	private final JLabel lblItemName = new JLabel();
	
	/**
	 * The label that contains the item stats.
	 */
	private final JLabel lblStats = new JLabel();
	
	/**
	 * The label that contains the item "use effect".
	 */
	private final JLabel lblUse = new JLabel();
	
	/**
	 * The label that contains item tags like "[Armorclass]" or "[ilvl]".
	 */
	private final JLabel lblTags = new JLabel();
	
	/**
	 * The separator between item name and module list.
	 */
	private final JSeparator separator = new JSeparator();
	
	/**
	 * The first GridBag row of the module list.
	 */
	private static final int MODULES_FIRST_ROW = 6;
	
	/**
	 * The horizontal addition space for the JLabels.
	 */
	private static final int HORIZONTAL_ADDITIONAL_SPACE = 20;
	
	/**
	 * The preferred height of the JLabels.
	 */
	private static final int LABEL_HEIGHT = 20;
	
	/**
	 * The spacing between components.
	 */
	private static final int SPACING = 5;
	
	/**
	 * The edge length of the module icons.
	 */
	private static final int ICON_EDGE_LENGTH = 30;
	
	/**
	 * The image loader.
	 */
	private final ImageLoader il = new ImageLoader();
	
	/**
	 * The string helper.
	 */
	private final DroidStringWorker sw = new DroidStringWorker();
	
	/**
	 * Create a new item ToolTip. Important: The ToolTip has to use a
	 * BorderLayout manager to work. The content panel has an own manager.
	 */
	public ItemToolTip() {
		super();

		this.setLayout(new BorderLayout());
		this.buildContentPanel();
		this.buildItemTypeLabel();
	}

	/**
	 * Build the item type label.
	 */
	private void buildItemTypeLabel() {
		this.c.gridx = 0;
		this.c.gridy = 0;
		this.c.weightx = 1;
		this.c.weighty = 0;
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.insets = new Insets(SPACING, SPACING, SPACING, SPACING);
		this.p.add(this.lblItemType, this.c);
	}

	/**
	 * Build the content panel.
	 */
	private void buildContentPanel() {
		this.p.setLayout(new GridBagLayout());
		this.p.setBackground(BABY_BLUE);
		this.p.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
		this.add(this.p);
	}
	
	/**
	 * Set the item to show information about. The argument can be null.
	 * @param i The item.
	 */
	public void setItem(final Item i) {
		this.item = i;
		
		this.removeComponents();
		
		if (this.item != null) {
			this.buildItemNameLabel();
			this.buildTagsLabel();
			this.buildStatsLabel();
			if (this.item.getUseText() != null) {
				this.buildUseEffectLabel();
			}
		}
		
		if (this.item != null && this.item.getModules().size() > 0) {
			this.buildModuleSeparator();
			this.buildModuleLabels();			
		}
	}
	
	/**
	 * Add the label that contains the item tags.
	 */
	private void buildTagsLabel() {
		this.lblTags.setText(this.item.getTagsText());
		this.lblTags.setForeground(Color.GRAY);
		this.c.gridx = 0;
		this.c.gridy = 2;
		this.c.weightx = 1;
		this.c.weighty = 0;
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.insets = new Insets(0, SPACING, SPACING, SPACING);
		this.p.add(this.lblTags, this.c);
	}

	/**
	 * Add the label that contains the item stats.
	 */
	private void buildStatsLabel() {
		this.lblStats.setText(this.item.getTotalStatsTextHTML());
		this.c.gridx = 0;
		this.c.gridy = 3;
		this.c.weightx = 1;
		this.c.weighty = 0;
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.insets = new Insets(0, SPACING, SPACING, SPACING);
		this.p.add(this.lblStats, this.c);
	}
	
	/**
	 * Add the label that contains the item "use effect".
	 */
	private void buildUseEffectLabel() {
		this.lblUse.setText(this.item.getUseEffectTextHTML());
		this.lblUse.setForeground(new Color(0, 100, 0));
		this.c.gridx = 0;
		this.c.gridy = 4;
		this.c.weightx = 1;
		this.c.weighty = 0;
		this.c.fill = GridBagConstraints.HORIZONTAL;
		this.c.insets = new Insets(0, SPACING, SPACING, SPACING);
		this.p.add(this.lblUse, this.c);
	}

	/**
	 * Build the module labels.
	 */
	private void buildModuleLabels() {
		int k = MODULES_FIRST_ROW;
		this.c.insets = new Insets(0, SPACING, SPACING, SPACING);
		
		for (EModuleProficiency moduleType : this.item.getModules().keySet()) {
			final JLabel lbl = new JLabel();
			String title = null;
			Item module = this.item.getModules().get(moduleType);
			
			
			if (module == null) {
				/*
				 * The module slot is empty but available.
				 */
				lbl.setIcon(this.il.getEmptyIcon30());
				title = moduleType.toString();
			} else {
				final String moduleImageName = module.getImageName();
				if (moduleImageName != null) {
					/*
					 * There is a named module equipped.
					 */
					lbl.setIcon(this.il.getSmallItemIcon(moduleImageName));
					title = module.getName() 
						+ " (" + module.getItemLevel() + ")";
				} else {
					/*
					 * There is an anonymous module equipped.
					 */
					lbl.setIcon(this.il.getItemIcon("anonymous_module"));
					title = moduleType.toString() 
						+ " (" + module.getItemLevel() + ")";
				}
			}
			
			final int lblWidth = 
				this.sw.textWidth(lbl, title)
				+ HORIZONTAL_ADDITIONAL_SPACE 
				+ ICON_EDGE_LENGTH + 2 * SPACING;
			lbl.setPreferredSize(new Dimension(lblWidth, ICON_EDGE_LENGTH));
			lbl.setText("<html>" + title + "</html>");
			this.c.gridy = k;
			this.p.add(lbl, this.c);
			k++;
		}
	}

	/**
	 * Build the module separator.
	 */
	private void buildModuleSeparator() {
		this.c.insets = new Insets(0, 0, SPACING, 0);
		this.c.gridy = 5;
		this.p.add(this.separator, this.c);
	}

	/**
	 * Build the item name label.
	 */
	private void buildItemNameLabel() {
		final int nameWidth = 
			this.sw.textWidth(this.lblItemName, this.item.getName());
		this.lblItemName.setPreferredSize(
					new Dimension(
							nameWidth + HORIZONTAL_ADDITIONAL_SPACE, 
							LABEL_HEIGHT));
		if (this.item.getRarity() != null) {
			this.lblItemName.setForeground(this.item.getRarity().getColor());
		}
		
		this.lblItemName.setHorizontalAlignment(SwingConstants.CENTER);
		this.c.gridy = 1;
		this.lblItemName.setText(this.item.getName());
		this.p.add(this.lblItemName, this.c);
	}

	/**
	 * Cleanup the module labels. Make sure that the item is not null before
	 * calling this.
	 */
	private void removeComponents() {
		final LinkedList<Component> remove = new LinkedList<Component>();
		
		for (Component component : this.p.getComponents()) {
			if (component != this.lblItemType) {
				remove.add(component);
			}
		}
		
		for (Component component : remove) {
				this.p.remove(component);
		}
	}

	@Override
	public Dimension getPreferredSize() {
		return this.p.getPreferredSize();
	}
	
	@Override
	public void setTipText(final String tipText) {
		if (tipText != null && !tipText.isEmpty()) {
			String text = "<html><i>" + tipText + "</i></html>";
			this.lblItemType.setText(text);
			final int tipTextWidth = 
				this.sw.textWidth(this.lblItemType, tipText);
			this.lblItemType.setPreferredSize(new Dimension(
								tipTextWidth + HORIZONTAL_ADDITIONAL_SPACE, 
								LABEL_HEIGHT));

			this.lblItemType.setHorizontalAlignment(SwingConstants.CENTER);
		} else {
			super.setTipText(tipText);
		}
	}
}
